#include "common/pch.h"

#include <format>

#include <cftc/reflect/class.h>
#include <cftc/reflect/field.h>
#include <cftc/util/strutils.h>
#include <cftf/loger/syslog.h>

#include "object/object.h"

namespace battle::object
{

	CFT_IMPLEMENT_REFLECT_CLASS_BEGIN(Object, cftc::reflect::Object);
	CFT_IMPLEMENT_REFLECT_CLASS_END();

    Object::Object()
    {
        m_objectTypeId = TYPEID_OBJECT;
        m_objectType = TYPEMASK_OBJECT;

        m_uint32Values = nullptr;
        m_valuesCount = 0;

        m_inWorld = false;
        m_objectUpdated = false;
    }

    Object::~Object()
    {
        if (IsInWorld())
        {
            ///- Do NOT call RemoveFromWorld here, if the object is a player it will crash
            cftf::loger::SysLog::Instance().Error(std::format("Object::~Object (GUID: {} TypeId: {}) deleted but still in world!!", GetGUIDLow(), GetTypeId()));
            CFT_ASSERT(false);
        }

        if (m_objectUpdated)
        {
            cftf::loger::SysLog::Instance().Error(std::format("Object::~Object (GUID: {} TypeId: {}) deleted but still have updated status!!", GetGUIDLow(), GetTypeId()));
            CFT_ASSERT(false);
        }

        delete[] m_uint32Values;
    }

    void Object::_InitValues()
    {
        m_uint32Values = new unsigned int[m_valuesCount];
        memset(m_uint32Values, 0, m_valuesCount * sizeof(unsigned int));

        m_changedValues.resize(m_valuesCount, false);

        m_objectUpdated = false;
    }

    void Object::_Create(unsigned int guidlow, unsigned int entry, HighGuid guidhigh)
    {
        if (!m_uint32Values)
        {
            _InitValues();
        }

        ObjectGuid guid = ObjectGuid(guidhigh, entry, guidlow);
        SetGuidValue(OBJECT_FIELD_GUID, guid);
        SetUint32Value(OBJECT_FIELD_TYPE, m_objectType);
    }

    void Object::_ReCreate(unsigned int entry)
    {
        if (!m_uint32Values)
        {
            _InitValues();
        }

        SetUint32Value(OBJECT_FIELD_TYPE, m_objectType);
        SetUint32Value(OBJECT_FIELD_ENTRY, entry);
    }

    void Object::SetObjectScale(float newScale)
    {
        SetFloatValue(OBJECT_FIELD_SCALE_X, newScale);
    }

    void Object::SendForcedObjectUpdate()
    {
        if (!m_inWorld || !m_objectUpdated)
        {
            return;
        }

#if 0
        UpdateDataMapType update_players;

        BuildUpdateData(update_players);
        RemoveFromClientUpdateList();

        WorldPacket packet;                                     // here we allocate a std::vector with a size of 0x10000
        for (UpdateDataMapType::iterator iter = update_players.begin(); iter != update_players.end(); ++iter)
        {
            iter->second.BuildPacket(&packet);
            iter->first->GetSession()->SendPacket(&packet);
            packet.clear();                                     // clean the string
        }
#endif
    }

#if 0
    void Object::BuildCreateUpdateBlockForPlayer(UpdateData* data, Player* target) const
    {
        if (!target)
        {
            return;
        }

        unsigned char  updatetype = UPDATETYPE_CREATE_OBJECT;
        unsigned char updateFlags = m_updateFlag;

        /** lower flag1 **/
        if (target == this)                                     // building packet for yourself
        {
            updateFlags |= UPDATEFLAG_SELF;
        }

        if (m_isNewObject)
        {
            switch (GetObjectGuid().GetHigh())
            {
            case HighGuid::HIGHGUID_DYNAMICOBJECT:
            case HighGuid::HIGHGUID_CORPSE:
            case HighGuid::HIGHGUID_PLAYER:
            case HighGuid::HIGHGUID_UNIT:
            case HighGuid::HIGHGUID_GAMEOBJECT:
                updatetype = UPDATETYPE_CREATE_OBJECT2;
                break;
            default:
                break;
            }
        }

        // DEBUG_LOG("BuildCreateUpdate: update-type: %u, object-type: %u got updateFlags: %X", updatetype, m_objectTypeId, updateFlags);

        ByteBuffer& buf = data->GetBuffer();
        buf << unsigned char(updatetype);
        buf << GetPackGUID();
        buf << unsigned char(m_objectTypeId);

        BuildMovementUpdate(&buf, updateFlags);

        UpdateMask updateMask;
        updateMask.SetCount(m_valuesCount);
        _SetCreateBits(&updateMask, target);
        BuildValuesUpdate(updatetype, &buf, &updateMask, target);
        data->AddUpdateBlock();
    }
#endif

    void Object::SendCreateUpdateToPlayer(Player* player)
    {
#if 0
        // send create update to player
        UpdateData upd;
        WorldPacket packet;

        BuildCreateUpdateBlockForPlayer(&upd, player);
        upd.BuildPacket(&packet);
        player->GetSession()->SendPacket(&packet);
#endif
    }

#if 0
    void Object::BuildValuesUpdateBlockForPlayer(UpdateData* data, Player* target) const
    {
        ByteBuffer& buf = data->GetBuffer();

        buf << unsigned char(UPDATETYPE_VALUES);
        buf << GetPackGUID();

        UpdateMask updateMask;
        updateMask.SetCount(m_valuesCount);

        _SetUpdateBits(&updateMask, target);
        BuildValuesUpdate(UPDATETYPE_VALUES, &buf, &updateMask, target);

        data->AddUpdateBlock();
    }

    void Object::BuildOutOfRangeUpdateBlock(UpdateData* data) const
    {
        data->AddOutOfRangeGUID(GetObjectGuid());
    }
#endif

    void Object::DestroyForPlayer(Player* target) const
    {
#if 0
        CFT_ASSERT(target);

        WorldPacket data(SMSG_DESTROY_OBJECT, 8);
        data << GetObjectGuid();
        target->GetSession()->SendPacket(&data);
#endif
    }

#if 0
    void Object::BuildMovementUpdate(ByteBuffer* data, unsigned char updateFlags) const
    {
        Unit const* unit = NULL;
        unsigned int highGuid = 0;
        MovementFlags moveflags = MOVEFLAG_NONE;

        switch (m_objectTypeId)
        {
        case TYPEID_OBJECT:
        case TYPEID_ITEM:
        case TYPEID_CONTAINER:
        case TYPEID_GAMEOBJECT:
        case TYPEID_DYNAMICOBJECT:
        case TYPEID_CORPSE:
            highGuid = unsigned int(GetObjectGuid().GetHigh());
            break;

        case TYPEID_PLAYER:
            // TODO: this code must not be here
            if (static_cast<Player const*>(this)->GetTransport())
            {
                ((Unit*)this)->m_movementInfo.AddMovementFlag(MOVEFLAG_ONTRANSPORT);
            }
            else
            {
                ((Unit*)this)->m_movementInfo.RemoveMovementFlag(MOVEFLAG_ONTRANSPORT);
            }

        case TYPEID_UNIT:
            unit = static_cast<Unit const*>(this);
            moveflags = unit->m_movementInfo.GetMovementFlags();
            break;

        default:
            break;
        }

        *data << unsigned char(updateFlags);

        if (updateFlags & UPDATEFLAG_LIVING)
        {
            CFT_ASSERT(unit);
            if (unit->IsStopped() && unit->m_movementInfo.HasMovementFlag(MOVEFLAG_SPLINE_ENABLED))
            {
                sLog.outError("%s is not moving but have spline movement enabled!", GetGuidStr().c_str());
                ((Unit*)this)->m_movementInfo.RemoveMovementFlag(MovementFlags(MOVEFLAG_SPLINE_ENABLED | MOVEFLAG_FORWARD));
            }

            *data << unit->m_movementInfo;
            // Unit speeds
            *data << float(unit->GetSpeed(MOVE_WALK));
            *data << float(unit->GetSpeed(MOVE_RUN));
            *data << float(unit->GetSpeed(MOVE_RUN_BACK));
            *data << float(unit->GetSpeed(MOVE_SWIM));
            *data << float(unit->GetSpeed(MOVE_SWIM_BACK));
            *data << float(unit->GetSpeed(MOVE_TURN_RATE));

            if (unit->m_movementInfo.HasMovementFlag(MOVEFLAG_SPLINE_ENABLED))
            {
                Movement::PacketBuilder::WriteCreate(*unit->movespline, *data);
            }
        }
        else if (updateFlags & UPDATEFLAG_HAS_POSITION)
        {
            *data << ((WorldObject*)this)->GetPositionX();
            *data << ((WorldObject*)this)->GetPositionY();
            *data << ((WorldObject*)this)->GetPositionZ();
            *data << ((WorldObject*)this)->GetOrientation();
        }

        if (updateFlags & UPDATEFLAG_HIGHGUID)
        {
            *data << highGuid;
        }

        if (updateFlags & UPDATEFLAG_ALL)
        {
            *data << (unsigned int)0x1;
        }

        if (updateFlags & UPDATEFLAG_FULLGUID)
        {
            if (unit && unit->getVictim())
            {
                *data << unit->getVictim()->GetPackGUID();
            }
            else
            {
                data->appendPackGUID(0);
            }
        }

        // 0x2
        if (updateFlags & UPDATEFLAG_TRANSPORT)
        {
            *data << unsigned int(GameTime::GetGameTimeMS());           // ms time
        }
    }
#endif

#if 0
    void Object::BuildValuesUpdate(unsigned char updatetype, ByteBuffer* data, UpdateMask* updateMask, Player* target) const
    {
        if (!target)
        {
            return;
        }

        bool IsActivateToQuest = false;
        if (isType(TYPEMASK_GAMEOBJECT) && !((GameObject*)this)->IsTransport())
        {
            IsActivateToQuest = ((GameObject*)this)->ActivateToQuest(target) || target->isGameMaster();

            updateMask->SetBit(GAMEOBJECT_DYN_FLAGS);
            if (updatetype == UPDATETYPE_VALUES)
            {
                updateMask->SetBit(GAMEOBJECT_ANIMPROGRESS);
            }
        }

        CFT_ASSERT(updateMask && updateMask->GetCount() == m_valuesCount);

        *data << (uint8)updateMask->GetBlockCount();
        updateMask->AppendToPacket(data);

        // 2 specialized loops for speed optimization in non-unit case
        if (isType(TYPEMASK_UNIT))                              // unit (creature/player) case
        {
            for (unsigned short index = 0; index < m_valuesCount; ++index)
            {
                if (updateMask->GetBit(index))
                {
                    if (index == UNIT_NPC_FLAGS)
                    {
                        unsigned int appendValue = m_uint32Values[index];

                        if (GetTypeId() == TYPEID_UNIT)
                        {
                            if (appendValue & UNIT_NPC_FLAG_TRAINER)
                            {
                                if (!((Creature*)this)->IsTrainerOf(target, false))
                                {
                                    appendValue &= ~UNIT_NPC_FLAG_TRAINER;
                                }
                            }

                            if (appendValue & UNIT_NPC_FLAG_STABLEMASTER)
                            {
                                if (target->getClass() != CLASS_HUNTER)
                                {
                                    appendValue &= ~UNIT_NPC_FLAG_STABLEMASTER;
                                }
                            }
                        }

                        *data << unsigned int(appendValue);
                    }
                    // FIXME: Some values at server stored in float format but must be sent to client in unsigned int format
                    else if (index >= UNIT_FIELD_BASEATTACKTIME && index <= UNIT_FIELD_RANGEDATTACKTIME)
                    {
                        // convert from float to unsigned int and send
                        *data << unsigned int(m_floatValues[index] < 0 ? 0 : m_floatValues[index]);
                    }

                    // there are some float values which may be negative or can't get negative due to other checks
                    else if ((index >= PLAYER_FIELD_NEGSTAT0 && index <= PLAYER_FIELD_NEGSTAT4) ||
                        (index >= PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE && index <= (PLAYER_FIELD_RESISTANCEBUFFMODSPOSITIVE + 6)) ||
                        (index >= PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE && index <= (PLAYER_FIELD_RESISTANCEBUFFMODSNEGATIVE + 6)) ||
                        (index >= PLAYER_FIELD_POSSTAT0 && index <= PLAYER_FIELD_POSSTAT4))
                    {
                        *data << unsigned int(m_floatValues[index]);
                    }

                    // Gamemasters should be always able to select units - remove not selectable flag
                    else if (index == UNIT_FIELD_FLAGS && target->isGameMaster())
                    {
                        *data << (m_uint32Values[index] & ~UNIT_FLAG_NOT_SELECTABLE);
                    }
                    /* Hide loot animation for players that aren't permitted to loot the corpse */
                    else if (index == UNIT_DYNAMIC_FLAGS && GetTypeId() == TYPEID_UNIT)
                    {
                        unsigned int send_value = m_uint32Values[index];

                        /* Initiate pointer to creature so we can check loot */
                        if (Creature* my_creature = (Creature*)this)
                        {
                            /* If the creature is NOT fully looted */
                            if (!my_creature->loot.isLooted())
                            {
                                /* If the lootable flag is NOT set */
                                if (!(send_value & UNIT_DYNFLAG_LOOTABLE))
                                {
                                    /* Update it on the creature */
                                    my_creature->SetFlag(UNIT_DYNAMIC_FLAGS, UNIT_DYNFLAG_LOOTABLE);
                                    /* Update it in the packet */
                                    send_value = send_value | UNIT_DYNFLAG_LOOTABLE;
                                }
                            }
                        }
                        /* If we're not allowed to loot the target, destroy the lootable flag */
                        if (!target->isAllowedToLoot((Creature*)this))
                        {
                            if (send_value & UNIT_DYNFLAG_LOOTABLE)
                            {
                                send_value = send_value & ~UNIT_DYNFLAG_LOOTABLE;
                            }
                        }

                        /* If we are allowed to loot it and mob is tapped by us, destroy the tapped flag */
                        bool is_tapped = target->IsTappedByMeOrMyGroup((Creature*)this);

                        /* If the creature has tapped flag but is tapped by us, remove the flag */
                        if (send_value & UNIT_DYNFLAG_TAPPED && is_tapped)
                        {
                            send_value = send_value & ~UNIT_DYNFLAG_TAPPED;
                        }

                        // Checking SPELL_AURA_EMPATHY and caster
                        if (send_value & UNIT_DYNFLAG_SPECIALINFO && ((Unit*)this)->IsAlive())
                        {
                            bool bIsEmpathy = false;
                            bool bIsCaster = false;
                            Unit::AuraList const& mAuraEmpathy = ((Unit*)this)->GetAurasByType(SPELL_AURA_EMPATHY);
                            for (Unit::AuraList::const_iterator itr = mAuraEmpathy.begin(); !bIsCaster && itr != mAuraEmpathy.end(); ++itr)
                            {
                                bIsEmpathy = true; // Empathy by aura set
                                if ((*itr)->GetCasterGuid() == target->GetObjectGuid())
                                {
                                    bIsCaster = true; // target is the caster of an empathy aura
                                }
                            }
                            if (bIsEmpathy && !bIsCaster) // Empathy by aura, but target is not the caster
                            {
                                send_value &= ~UNIT_DYNFLAG_SPECIALINFO;
                            }
                        }

                        *data << send_value;
                    }
                    else                                        // Unhandled index, just send
                    {
                        // send in current format (float as float, unsigned int as uint32)
                        *data << m_uint32Values[index];
                    }
                }
            }
        }
        else if (isType(TYPEMASK_GAMEOBJECT))                   // gameobject case
        {
            for (unsigned short index = 0; index < m_valuesCount; ++index)
            {
                if (updateMask->GetBit(index))
                {
                    // send in current format (float as float, unsigned int as uint32)
                    if (index == GAMEOBJECT_DYN_FLAGS)
                    {
                        if (IsActivateToQuest)
                        {
                            switch (((GameObject*)this)->GetGoType())
                            {
                            case GAMEOBJECT_TYPE_QUESTGIVER:
                            case GAMEOBJECT_TYPE_CHEST:
                            case GAMEOBJECT_TYPE_GENERIC:
                            case GAMEOBJECT_TYPE_SPELL_FOCUS:
                            case GAMEOBJECT_TYPE_GOOBER:
                                *data << unsigned short(GO_DYNFLAG_LO_ACTIVATE);
                                *data << unsigned short(0);
                                break;
                            default:
                                *data << unsigned int(0);         // unknown, not happen.
                                break;
                            }
                        }
                        else
                        {
                            // disable quest object
                            *data << unsigned int(0);
                        }
                    }
                    else
                    {
                        *data << m_uint32Values[index];          // other cases
                    }
                }
            }
        }
        else                                                    // other objects case (no special index checks)
        {
            for (unsigned short index = 0; index < m_valuesCount; ++index)
            {
                if (updateMask->GetBit(index))
                {
                    // send in current format (float as float, unsigned int as uint32)
                    *data << m_uint32Values[index];
                }
            }
        }
    }
#endif

    void Object::ClearUpdateMask(bool remove)
    {
        if (m_uint32Values)
        {
            for (unsigned short index = 0; index < m_valuesCount; ++index)
            {
                m_changedValues[index] = false;
            }
        }

        if (m_objectUpdated)
        {
            if (remove)
            {
                RemoveFromClientUpdateList();
            }
            m_objectUpdated = false;
        }
    }

    bool Object::LoadValues(const char* data)
    {
        if (!m_uint32Values)
        {
            _InitValues();
        }

        std::vector<std::string> tmp_strs;
        cftc::util::Split(data, tmp_strs, ' ');

        if (tmp_strs.size() != m_valuesCount)
        {
            return false;
        }
        for (std::size_t i = 0; i < tmp_strs.size(); ++i) {
            m_uint32Values[i] = cftc::ducktype::XValue(tmp_strs[i]);
        }

        return true;
    }

#if 0
    void Object::_SetUpdateBits(UpdateMask* updateMask, Player* /*target*/) const
    {
        for (unsigned short index = 0; index < m_valuesCount; ++index)
            if (m_changedValues[index])
            {
                updateMask->SetBit(index);
            }
    }

    void Object::_SetCreateBits(UpdateMask* updateMask, Player* /*target*/) const
    {
        for (unsigned short index = 0; index < m_valuesCount; ++index)
            if (GetUInt32Value(index) != 0)
            {
                updateMask->SetBit(index);
            }
    }
#endif

    void Object::SetInt32Value(unsigned short index, int value)
    {
#if 0
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        if (m_int32Values[index] != value)
        {
            m_int32Values[index] = value;
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
#endif
    }

    void Object::SetUint32Value(unsigned short index, unsigned int value)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        if (m_uint32Values[index] != value)
        {
            m_uint32Values[index] = value;
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    void Object::UpdateUint32Value(unsigned short index, unsigned int value)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        m_uint32Values[index] = value;
        m_changedValues[index] = true;
    }

    void Object::SetUint64Value(unsigned short index, const unsigned long long& value)
    {
        CFT_ASSERT(index + 1 < m_valuesCount || PrintIndexError(index, true));
        if (*((unsigned long long*)&(m_uint32Values[index])) != value)
        {
            m_uint32Values[index] = *((unsigned int*)&value);
            m_uint32Values[index + 1] = *(((unsigned int*)&value) + 1);
            m_changedValues[index] = true;
            m_changedValues[index + 1] = true;
            MarkForClientUpdate();
        }
    }

    void Object::SetFloatValue(unsigned short index, float value)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        if (m_floatValues[index] != value)
        {
            m_floatValues[index] = value;
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    void Object::SetByteValue(unsigned short index, unsigned char offset, unsigned char value)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        if (offset > 4)
        {
            cftf::loger::SysLog::Instance().Error(std::format("Object::SetByteValue: wrong offset {}", offset));
            return;
        }

        if (unsigned char(m_uint32Values[index] >> (offset * 8)) != value)
        {
            m_uint32Values[index] &= ~unsigned int(unsigned int(0xFF) << (offset * 8));
            m_uint32Values[index] |= unsigned int(unsigned int(value) << (offset * 8));
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    void Object::SetUInt16Value(unsigned short index, unsigned char offset, unsigned short value)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        if (offset > 2)
        {
            cftf::loger::SysLog::Instance().Error(std::format("Object::SetUint16Value: wrong offset {}", offset));
            return;
        }

        if (unsigned short(m_uint32Values[index] >> (offset * 16)) != value)
        {
            m_uint32Values[index] &= ~unsigned int(unsigned int(0xFFFF) << (offset * 16));
            m_uint32Values[index] |= unsigned int(unsigned int(value) << (offset * 16));
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    void Object::SetStatFloatValue(unsigned short index, float value)
    {
        if (value < 0)
        {
            value = 0.0f;
        }

        SetFloatValue(index, value);
    }

    void Object::SetStatInt32Value(unsigned short index, int value)
    {
        if (value < 0)
        {
            value = 0;
        }

        SetUint32Value(index, unsigned int(value));
    }

    void Object::ApplyModUint32Value(unsigned short index, int val, bool apply)
    {
        int cur = GetUint32Value(index);
        cur += (apply ? val : -val);
        if (cur < 0)
        {
            cur = 0;
        }
        SetUint32Value(index, cur);
    }

    void Object::ApplyModInt32Value(unsigned short index, int val, bool apply)
    {
        int cur = GetInt32Value(index);
        cur += (apply ? val : -val);
        SetInt32Value(index, cur);
    }

    void Object::ApplyModSignedFloatValue(unsigned short index, float  val, bool apply)
    {
        float cur = GetFloatValue(index);
        cur += (apply ? val : -val);
        SetFloatValue(index, cur);
    }

    void Object::ApplyModPositiveFloatValue(unsigned short index, float  val, bool apply)
    {
        float cur = GetFloatValue(index);
        cur += (apply ? val : -val);
        if (cur < 0)
        {
            cur = 0;
        }
        SetFloatValue(index, cur);
    }

    void Object::MarkFlagUpdateForClient(unsigned short index)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        m_changedValues[index] = true;
        MarkForClientUpdate();
    }

    void Object::ForceValuesUpdateAtIndex(unsigned short index)
    {
        m_changedValues[index] = true;
        if (m_inWorld && !m_objectUpdated)
        {
            AddToClientUpdateList();
            m_objectUpdated = true;
        }
    }

    void Object::SetFlag(unsigned short index, unsigned int newFlag)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
        unsigned int oldval = m_uint32Values[index];
        unsigned int newval = oldval | newFlag;

        if (oldval != newval)
        {
            m_uint32Values[index] = newval;
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    void Object::RemoveFlag(unsigned short index, unsigned int oldFlag)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));
        unsigned int oldval = m_uint32Values[index];
        unsigned int newval = oldval & ~oldFlag;

        if (oldval != newval)
        {
            m_uint32Values[index] = newval;
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    void Object::SetByteFlag(unsigned short index, unsigned char offset, unsigned char newFlag)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        if (offset > 4)
        {
            cftf::loger::SysLog::Instance().Error(std::format("Object::SetByteFlag: wrong offset {}", offset));
            return;
        }

        if (!(unsigned char(m_uint32Values[index] >> (offset * 8)) & newFlag))
        {
            m_uint32Values[index] |= unsigned int(unsigned int(newFlag) << (offset * 8));
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    void Object::RemoveByteFlag(unsigned short index, unsigned char offset, unsigned char oldFlag)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        if (offset > 4)
        {
            cftf::loger::SysLog::Instance().Error(std::format("Object::RemoveByteFlag: wrong offset {}", offset));
            return;
        }

        if (unsigned char(m_uint32Values[index] >> (offset * 8)) & oldFlag)
        {
            m_uint32Values[index] &= ~unsigned int(unsigned int(oldFlag) << (offset * 8));
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    void Object::SetShortFlag(unsigned short index, bool highpart, unsigned short newFlag)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        if (!(unsigned short(m_uint32Values[index] >> (highpart ? 16 : 0)) & newFlag))
        {
            m_uint32Values[index] |= unsigned int(unsigned int(newFlag) << (highpart ? 16 : 0));
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    void Object::RemoveShortFlag(unsigned short index, bool highpart, unsigned short oldFlag)
    {
        CFT_ASSERT(index < m_valuesCount || PrintIndexError(index, true));

        if (unsigned short(m_uint32Values[index] >> (highpart ? 16 : 0)) & oldFlag)
        {
            m_uint32Values[index] &= ~unsigned int(unsigned int(oldFlag) << (highpart ? 16 : 0));
            m_changedValues[index] = true;
            MarkForClientUpdate();
        }
    }

    bool Object::PrintIndexError(unsigned int index, bool set) const
    {
        cftf::loger::SysLog::Instance().Error(std::format("Attempt {} nonexistent value field: {} (count: {}) for object typeid: {} type mask: {}", (set ? "set value to" : "get value from"), index, m_valuesCount, GetTypeId(), m_objectType));
        // ASSERT must fail after function call
        return false;
    }

    bool Object::PrintEntryError(char const* descr) const
    {
        cftf::loger::SysLog::Instance().Error(std::format("Object Type {}, Entry {} (lowguid {}) with invalid call for {}", GetTypeId(), GetEntry(), GetObjectGuid().GetCounter(), descr));

        // always false for continue assert fail
        return false;
    }
#if 0
    void Object::BuildUpdateDataForPlayer(Player* pl, UpdateDataMapType& update_players)
    {
        UpdateDataMapType::iterator iter = update_players.find(pl);

        if (iter == update_players.end())
        {
            std::pair<UpdateDataMapType::iterator, bool> p = update_players.insert(UpdateDataMapType::value_type(pl, UpdateData()));
            CFT_ASSERT(p.second);
            iter = p.first;
        }

        BuildValuesUpdateBlockForPlayer(&iter->second, iter->first);
    }
#endif

    void Object::AddToClientUpdateList()
    {
        cftf::loger::SysLog::Instance().Error(std::format("Unexpected call of Object::AddToClientUpdateList for object (TypeId: {} Update fields: {})", GetTypeId(), m_valuesCount));
        CFT_ASSERT(false);
    }

    void Object::RemoveFromClientUpdateList()
    {
        cftf::loger::SysLog::Instance().Error(std::format("Unexpected call of Object::RemoveFromClientUpdateList for object (TypeId: {} Update fields: {})", GetTypeId(), m_valuesCount));
        CFT_ASSERT(false);
    }
#if 0
    void Object::BuildUpdateData(UpdateDataMapType& /*update_players */)
    {
        sLog.outError("Unexpected call of Object::BuildUpdateData for object (TypeId: %u Update fields: %u)", GetTypeId(), m_valuesCount);
        CFT_ASSERT(false);
    }
#endif

    void Object::MarkForClientUpdate()
    {
        if (m_inWorld)
        {
            if (!m_objectUpdated)
            {
                AddToClientUpdateList();
                m_objectUpdated = true;
            }
        }
    }

}